﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using SystemWnioskujacy.Exceptions;
using System.Globalization;

namespace SystemWnioskujacy.Parser {
    public static class Parser {
        public static NumberFormatInfo NumberFormat = new CultureInfo("en-GB").NumberFormat;

        public static string Input { get; private set; }

        public static void Parse(string input) {
            Input = input;

            string[] tokens = Tokenize(Input);
            int ind = 0;

            for (int l = 0; l < tokens.Length; l++) {
                string token = tokens[l];

                switch (token) {
                    case Consts.Assert: {
                            ind = ParseAssert(tokens, l + 1);
                        } break;
                    case Consts.DefFacts: {
                            ind = ParseDefFacts(tokens, l + 1);
                        } break;
                    case Consts.Deassert: {
                            ind = ParseDeassert(tokens, l + 1);
                        } break;
                    case Consts.DefRule: {
                            ind = ParseDefRule(tokens, l + 1);
                        } break;
                    case Consts.Print: {
                            ind = ParsePrint(tokens, l + 1);
                        } break;
                    case Consts.Println: {
                            ind = ParsePrint(tokens, l + 1, true);
                        } break;
                    case Consts.Facts: {
                            Function.Facts();
                        } break;
                    case Consts.Rules: {
                            Function.Rules();
                        } break;
                    case Consts.Run: {
                            Function.Run();
                        } break;
                    case Consts.RunWithoutUncertainty:
                    case Consts.Run2: {
                            Function.RunWithoutUncertainty();
                        } break;
                    case Consts.Clear: {
                            Function.Clear();
                        } break;
                    case Consts.ClearScreen:
                    case Consts.Cls: {
                            Function.ClearScreen();
                        } break;
                    case Consts.Quit: {
                            Function.Quit(true);
                        } break;
                    default:
                        throw new KeywordNotExistsException(token);
                };

                l = ind;
            }

        }

        private static int ParsePrint(string[] tokens, int startInd = 0, bool newLineAfter = false) {
            int l = startInd;
            StringBuilder strB = new StringBuilder();
            bool finished = false;

            for (; l < tokens.Length; l++) {
                string tt = tokens[l];

                if ((l == startInd) && (tt == "(")) {
                    continue;
                }
                else if ((l == startInd) && (tt != "(")) {
                    throw new SyntaxErrorException(string.Format("Missing parenthesis near {0} ... {1}", (newLineAfter) ? "println" : "print", tt));
                }
                else if ((l == startInd + 1) && (tt == "\"")) {
                    continue;
                }
                else if ((l == startInd + 1) && (tt != "\"")) {
                    throw new SyntaxErrorException(string.Format("Missing quote near {0} ... {1}", (newLineAfter) ? "println(" : "print(", tt));
                }

                if (tt == "\"") {
                    finished = true;
                    continue;
                }

                if (finished && (tt == ")")) {
                    l++;
                    break;
                }
                else if (!finished && (tt == ")")) {
                    throw new SyntaxErrorException(string.Format("Missing quote near {0} ... ... ^)", (newLineAfter) ? "println(" : "print("));
                }
                else if (finished && tt != ")") {
                    throw new SyntaxErrorException(string.Format("Missing parenthesis near {0} ... ... \"^ {1}", (newLineAfter) ? "println(" : "print(", tt));
                }

                if (l > startInd + 2) {
                    strB.Append(" ");
                }
                strB.Append(tt);
            }

            string str = strB.ToString();
            str = str.Replace("\\n", "\n");
            str = str.Replace("\\t", "\t");
            str = str.Replace("\\r", "\r");

            if (newLineAfter) {
                Function.Println(str);
            }
            else {
                Function.Print(str);
            }

            return l;
        }

        private static int ParseDeassert(string[] tokens, int startInd = 0) {
            int l = startInd;

            string tt = tokens[l++];
            if (tt != "(") {
                throw new SyntaxErrorException("Missing parenthesis exception near deassert ... " + tt);
            }
            if ((l + 1 >= tokens.Length) || (tokens[l + 1] != ")")) {
                throw new SyntaxErrorException("Missing parenthesis exception near deassert (... " + tokens[l]);
            }

            try {
                int id = int.Parse(tokens[l]);
                if (id < 0) {
                    throw new SyntaxErrorException(string.Format("Incorrect fact id ({0}) value!", id));
                }
            }
            catch (FormatException exc) {
                throw new SyntaxErrorException("Incorrect number format of fact id value!", exc);
            }
            catch (OverflowException exc) {
                throw new SyntaxErrorException("Overflow of fact id value!", exc);
            }

            l = startInd + 3;
            return l;
        }

        private static int ParseAssert(string[] tokens, int startInd = 0, bool isAsserCmd = true) {
            int l = startInd;
            StringBuilder strB = new StringBuilder();
            double grf = 1.0, irf = 1.0;

            for (; l < tokens.Length; l++) {
                string tt = tokens[l];

                if ((l == startInd) && (tt == "(")) {
                    continue;
                }
                else if ((l == startInd) && (tt != "(")) {
                    if (isAsserCmd) {
                        throw new SyntaxErrorException("Missing parenthesis near assert ... " + tt);
                    }
                    else {
                        throw new SyntaxErrorException("Missing parenthesis near deffacts ( ... ^" + tt);
                    }
                }

                if (l == startInd + 1) {
                    string[] tmp = { tokens[l], tokens[l + 1], tokens[l + 2], tokens[l + 3] };

                    if ((tmp[1] == ",") && (tmp[3] == ",")) {
                        try {
                            grf = double.Parse(tmp[0], NumberFormat);
                            irf = double.Parse(tmp[2], NumberFormat);

                            l += 3;
                            continue;
                        }
                        catch (FormatException exc) {
                            throw new SyntaxErrorException("Incorrect number format of GRF or IRF parameter!", exc);
                        }
                        catch (OverflowException exc) {
                            throw new SyntaxErrorException("Overflow of GRF or IRF parameter!", exc);
                        }
                    }
                    else if ((tmp[1] == ",") && (tmp[3] != ",")) {
                        throw new MissingParameterException("IRF");
                    }
                    else if (tmp[0] == ",") {
                        throw new MissingParameterException("GRF");
                    }
                }

                if (tt == ")") {
                    l++;
                    break;
                }

                if (strB.Length > 0) {
                    strB.Append(" ");
                }
                strB.Append(tt);
            }

            Base.Add(strB.ToString(), grf, irf);

            return l;
        }

        private static int ParseDefRule(string[] tokens, int startInd = 0) {
            int l = startInd;
            string ruleName = "";
            double grf = 1.0, irf = 1.0;
            Rule rule = new Rule();

            for (; l < tokens.Length; l++) {
                string tt = tokens[l];

                if ((l == startInd) & (Regex.IsMatch(tt, Consts.NamingRule_Default))) {
                    ruleName = tt;
                    continue;
                }
                else if ((l == startInd) & (!Regex.IsMatch(tt, Consts.NamingRule_Default))) {
                    throw new IncorrectNameException(string.Format("Incorrect name (\"{0}\") for rule.", tt));
                }
                if ((l == startInd + 1) && (tt == "(")) {

                }
                else if ((l == startInd + 1) && (tt != "(")) {
                    throw new SyntaxErrorException(string.Format("Missing parenthesis near defrule {0} ... {1}", ruleName, tt));
                }
                if (l == startInd + 2) {
                    string[] tmp = { tokens[l], tokens[l + 1], tokens[l + 2], tokens[l + 3] };

                    if ((tmp[1] == ",") && (tmp[3] == ",")) {
                        try {
                            grf = double.Parse(tmp[0], NumberFormat);
                            irf = double.Parse(tmp[2], NumberFormat);

                            l += 3;
                            continue;
                        }
                        catch (FormatException exc) {
                            throw new SyntaxErrorException("Incorrect number format of GRF or IRF parameter!", exc);
                        }
                        catch (OverflowException exc) {
                            throw new SyntaxErrorException("Overflow of GRF or IRF parameter!", exc);
                        }
                    }
                    else if ((tmp[1] == ",") && (tmp[3] != ",")) {
                        throw new MissingParameterException("IRF");
                    }
                    else if (tmp[0] == ",") {
                        throw new MissingParameterException("GRF");
                    }
                }

                // Reguła - lewa stronna
                while ((l + 1 < tokens.Length) && (tokens[l + 1] != "=>") && (tokens[l] != "=>")) {
                    l = ParseRuleLeft(rule, tokens, l);
                }
                if (tt == "=>") {
                    l++;
                    break;
                }

                // Reguła - prawa strona
                while ((l + 1 < tokens.Length) && (tokens[l] != ")")) {
                    l = ParseRuleRight(rule, tokens, l);
                }

                if (tt == ")") {
                    l++;
                    break;
                }
            }



            l++;
            return l;
        }

        private static int ParseRuleLeft(Rule rule, string[] tokens, int startInd = 0) {
            int l = startInd;
            


            return l;
        }

        private static int ParseRuleRight(Rule rule, string[] tokens, int startInd = 0) {
            int l = startInd;



            return l;
        }

        private static int ParseDefFacts(string[] tokens, int startInd = 0) {
            int l = startInd;
            string setOfFactsName = "";
            StringBuilder strB = new StringBuilder();

            for (; l < tokens.Length; l++) {
                string tt = tokens[l];

                if ((l == startInd) & (Regex.IsMatch(tt, Consts.NamingRule_Default))) {
                    setOfFactsName = tt;
                    continue;
                }
                else if ((l == startInd) & (!Regex.IsMatch(tt, Consts.NamingRule_Default))) {
                    throw new IncorrectNameException(string.Format("Incorrect name(\"{0}\") for set of facts!", tt));
                }
                if ((l == startInd + 1) && (tt == "(")) {
                    continue;
                }
                else if ((l == startInd + 1) && (tt != "(")) {
                    throw new SyntaxErrorException(string.Format("Missing parenthesis near deffacts {0} ... {1}", setOfFactsName, tt));
                }

                // Parsuj fakty dopóki nie natrafisz na nawias domykający zbiór faktów
                do {
                    if (tokens[l] == ")") {
                        l++;
                        break;
                    }
                    l = ParseAssert(tokens, l, false);
                } while ((l < tokens.Length) && (tokens[l] != ")"));

                if (tokens[l] == ")") {
                    l++;
                    break;
                }
            }

            return l;
        }

        public static string[] Tokenize(string str) {
            string[] tmp = null;
            string[] res = null;

            // Wstępna tokenizacja z zachowaniem elementów rozdzielających (delimiters)
            tmp = Regex.Split(str, @"[ \t\r\n]+|(\()|(\))|(\,)|(\=>)");

            // Usunięcie elementów zawierających białe znaki
            res = tmp.Where(x => Regex.IsMatch(x, "[ \t\r\n]+") == false && x != "").ToArray<string>();

            return res;
        }
    }
}
